package be.dch.objectprops.parser;

import java.lang.reflect.Field;
import java.util.Map;

import org.apache.log4j.Logger;

import be.dch.objectprops.ParserFinder;
import be.dch.objectprops.collector.Collector;
import be.dch.objectprops.impl.ObjectUtil;

import com.google.inject.Inject;

public class MapParser implements Parser {

    private static final String PROP_SEPA = ".";
    private static final String PROP_TYPE = "type";
    private static final String PROP_VALUE = "value";
    private static final String PROP_VALUE_TYPE = "valueType";
    private static final String PROP_ENTRY = "entry";
    private static final String PROP_ENTRY_TYPE = "entryType";

    private static final Logger LOG = Logger.getLogger(MapParser.class);
    @Inject
    private Collector props;
    @Inject
    private ParserFinder finder;

    /*
     * (non-Javadoc)
     * 
     * @see be.dch.objectprops.parser.Parser#handleAsWriter(java.lang.Object)
     */
    
    public boolean handleAsWriter(final Object result) {
	if (result instanceof Map) {
	    return true;
	}
	return false;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * be.dch.objectprops.parser.Parser#handleAsReader(java.lang.reflect.Field)
     */
    
    public boolean handleAsReader(final Field field) {
	MapParser.LOG.debug("Investigating of object can be handled");
	if (field == null) {
	    return false;
	}
	return field.getType().getCanonicalName().equals(Map.class.getCanonicalName());
    }

    /*
     * (non-Javadoc)
     * 
     * @see be.dch.objectprops.parser.Parser#write(java.lang.Object,
     * java.lang.StringBuilder, be.dch.objectprops.impl.ObjectWriter,
     * be.dch.objectprops.collector.Collector)
     */
    
    public void write(final Object result, final StringBuilder key) {
	final Map<?, ?> mapResult = (Map<?, ?>) result;
	final int keyLength = key.toString().length();
	key.append(MapParser.PROP_SEPA);
	key.append(MapParser.PROP_TYPE);
	this.props.setProperty(key.toString(), result.getClass().getCanonicalName());
	int j = 0;
	for (final Object item : mapResult.keySet()) {
	    key.setLength(keyLength);
	    key.append('.');
	    key.append(j);
	    key.append(MapParser.PROP_SEPA);
	    key.append(MapParser.PROP_ENTRY);
	    this.finder.write(item, key);
	    key.setLength(keyLength);
	    key.append('.');
	    key.append(j);
	    key.append(MapParser.PROP_SEPA);
	    key.append(MapParser.PROP_ENTRY_TYPE);
	    this.props.setProperty(key.toString(), item.getClass().getCanonicalName());
	    key.setLength(keyLength);
	    key.append('.');
	    key.append(j);
	    key.append(MapParser.PROP_SEPA);
	    key.append(MapParser.PROP_VALUE);
	    final Object itemValue = mapResult.get(item);
	    this.finder.write(itemValue, key);
	    key.setLength(keyLength);
	    key.append('.');
	    key.append(j);
	    key.append(MapParser.PROP_SEPA);
	    key.append(MapParser.PROP_VALUE_TYPE);

	    this.props.setProperty(key.toString(), itemValue.getClass().getCanonicalName());

	    j++;
	}
    }

    /*
     * (non-Javadoc)
     * 
     * @see be.dch.objectprops.parser.Parser#read(java.lang.StringBuilder,
     * java.lang.reflect.Field, java.lang.Object,
     * be.dch.objectprops.impl.ObjectReader,
     * be.dch.objectprops.collector.Collector)
     */
    
    @SuppressWarnings("unchecked")
    public void read(final StringBuilder key, final Field map, final Object result) {
	final int initialLength = key.length();
	try {
	    key.append(MapParser.PROP_SEPA);
	    key.append(MapParser.PROP_TYPE);
	    final String clazz = this.props.getProperty(key.toString());
	    final Map<Object, Object> resultMap = (Map<Object, Object>) ObjectUtil.newInstance(clazz);
	    int j = 0;
	    key.setLength(initialLength);
	    key.append(MapParser.PROP_SEPA);
	    key.append(j);
	    while (this.props.startWithKey(key.toString())) {
		key.setLength(initialLength);
		key.append('.');
		key.append(j);
		key.append(MapParser.PROP_SEPA);
		key.append(MapParser.PROP_ENTRY);
		final String keyValue = key.toString();
		key.setLength(initialLength);
		key.append('.');
		key.append(j);
		key.append(MapParser.PROP_SEPA);
		key.append(MapParser.PROP_ENTRY_TYPE);
		final String keyType = this.props.getProperty(key.toString());
		Object keyTypeObject = ObjectUtil.newInstance(keyType);
		keyTypeObject = this.finder.read(new StringBuilder(keyValue), keyTypeObject);
		key.setLength(initialLength);
		key.append('.');
		key.append(j);
		key.append(MapParser.PROP_SEPA);
		key.append(MapParser.PROP_VALUE);
		final String value = key.toString();
		key.setLength(initialLength);
		key.append('.');
		key.append(j);
		key.append(MapParser.PROP_SEPA);
		key.append(MapParser.PROP_VALUE_TYPE);
		final String valueType = this.props.getProperty(key.toString());
		Object valueObject = ObjectUtil.newInstance(valueType);
		valueObject = this.finder.read(new StringBuilder(value), valueObject);
		resultMap.put(keyTypeObject, valueObject);
		j++;
		key.setLength(initialLength);
		key.append(MapParser.PROP_SEPA);
		key.append(j);
	    }
	    map.set(result, resultMap);
	} catch (final IllegalAccessException e) {
	    MapParser.LOG.error(e);
	}
	key.setLength(initialLength);
    }

    
    public Object read(final StringBuilder key, final Object result) {
	return null;
    }
}
